//
//  SFMovableTableViewController.m
//  TableViewFramework
//
//  Created by 支晓辉 on 16/3/25.
//  Copyright © 2016年 wishselfly. All rights reserved.
//

#import "SFMovableTableViewController.h"

#define NSStringFromIndexPath(path) [NSString stringWithFormat:@"section=%ld, row=%ld", path.section, path.row]

@interface SFMovableTableViewController () <SFMovableTableViewCellDelegate>
/** 当前正在移动的行 */
@property (copy, nonatomic) NSIndexPath *currentMoveIndexPath;
/** 开始移动的组(按下到松开前) */
@property (assign, nonatomic) NSInteger startMoveSection;

/** 当前视图的视图镜像 */
@property (weak, nonatomic) UIImageView *currentMovingView;
/** 长按之后开始移动的点 */
@property (nonatomic) CGPoint beginPoint;
/** 从哪个开始 */
@property (assign, nonatomic) NSInteger fromSection;
/** 到哪里结束 */
@property (assign, nonatomic) NSInteger toSection;
/** 用于防止多点触控 */
@property (weak, nonatomic) id moveTag;
/** 当前正在长按的手势识别器 */
@property (weak, nonatomic) UILongPressGestureRecognizer *currentLongPressGesture;
/** 正在移动的cell */
@property (weak, nonatomic) SFMovableTableViewCell *movingCell;
/** 自动滚动的计时器 */
@property (strong, nonatomic) NSTimer *autoScrollTimer;

@end

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
#pragma clang diagnostic ignored "-Wprotocol"

@implementation SFMovableTableViewController
{
    UIGestureRecognizerState state;
}
#pragma mark - init

- (instancetype)init {
    if (self = [super init]) {
        [self _initValues];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        [self _initValues];
    }
    return self;
}

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        [self _initValues];
    }
    return self;
}

#pragma mark - Life cycle

- (void)viewDidLoad {
    [super viewDidLoad];
    [self _initTableView];
}

#pragma mark - Public methods

#pragma mark Intersect cell

- (SFMovableTableViewCell*)intersetCellWithRect:(CGRect)globalRect {
    NSEnumerator *enumerator = self.tableView.visibleCells.reverseObjectEnumerator;
    SFMovableTableViewCell *cell;
    while ((cell = enumerator.nextObject)) {
        CGRect cellScreenRect = [cell.superview convertRect:cell.frame toView:[UIApplication sharedApplication].keyWindow];
        if (CGRectIntersectsRect(cellScreenRect, globalRect)) {
            return cell;
        }
    }
    return nil;
}

#pragma mark Auto scroll

- (void)tryToAutoScroll:(BOOL)isDown {
    if (self.autoScrollTimer) {
        if ([self.autoScrollTimer.userInfo boolValue] == isDown) {
            return;
        } else {
            [self stopAutoScrollTimerIfNeed];
            [self createTimerAutoScrollDown:isDown];
        }
    } else {
        [self createTimerAutoScrollDown:isDown];
    }
}

/**
 *  如果存在未被释放的自动滚动计时器，则自动释放
 */
- (void)stopAutoScrollTimerIfNeed {
    if (self.autoScrollTimer) {
        [self.autoScrollTimer invalidate];
        self.autoScrollTimer = nil;
    }
}

- (void)movableTableViewDidAutoScroll {
    
}

#pragma mark - Private methods

/**
 *  初始化一些基本值
 */
- (void)_initValues {
    self.startMoveSection =
    self.fromSection =
    self.toSection = -1;
}

/**
 *  初始化列表视图
 */
- (void)_initTableView {
    UITableView *tableView = [[UITableView alloc] init];
    self.view = tableView;
    tableView.delegate = self;
    tableView.dataSource = self;
}

#pragma mark - SFMovableTableViewCellDelegate

- (void)movableTableViewCell:(SFMovableTableViewCell*)cell moveTo:(CGRect)toRect withIndexPath:(NSIndexPath *)indexPath {
    //逆序遍历
    NSEnumerator *rEnum = self.tableView.visibleCells.reverseObjectEnumerator;
    SFMovableTableViewCell *c = nil;
    while ((c = rEnum.nextObject)) {
        BOOL isBottomCell = NO;
        c.currentIndexPath = [self.tableView indexPathForCell:c];
        if (c.currentIndexPath.section == self.currentMoveIndexPath.section &&
            c.currentIndexPath.row == self.currentMoveIndexPath.row) {
            continue;
        }
        CGRect tableViewRect = [self.view.superview convertRect:self.tableView.frame toView:[UIApplication sharedApplication].keyWindow];
        CGRect r = [c.superview convertRect:c.frame toView:[UIApplication sharedApplication].keyWindow];
        if (CGRectGetMaxY(r) > CGRectGetMaxY(tableViewRect)) {  //如果当前cell没有显示完全的时候不予交换
            isBottomCell = YES;
        }
        //跨组移动的时候
        if (CGRectIntersectsRect(r, toRect) && self.fromSection != c.currentIndexPath.section) {
            
            self.toSection = c.currentIndexPath.section;
            NSInteger sectionCount = [self.tableView numberOfRowsInSection:c.currentIndexPath.section];
            NSInteger srcSectionCount = [self.tableView numberOfRowsInSection:indexPath.section];
            NSIndexPath *intersectCellIndexPath = c.currentIndexPath.copy;
            /*
             *  等于1这种状况有可能是先跳到前一组的结尾，再跳回去后一组的队头，
             *  再跳回前一组队尾，再跳回后一组队头出现，因为队尾已经重新
             */
            if (c.currentIndexPath.row == 0 && sectionCount > 1) {  //从头进
                if (![self canMoveIndexPathFrom:indexPath toIndexPath:c.currentIndexPath])return;
                NSIndexPath *toIndexPath = c.currentIndexPath;
                self.currentMoveIndexPath = c.currentIndexPath.copy;
                cell.currentIndexPath = c.currentIndexPath.copy;
                [self moveRowFromIndexPath:indexPath toIndexPath:toIndexPath];
                [self.tableView moveRowAtIndexPath:indexPath toIndexPath:toIndexPath];
            } else if (c.currentIndexPath.row == sectionCount - 1) {                            //从尾进
                NSIndexPath *toIndexPath = [NSIndexPath indexPathForRow:sectionCount inSection:c.currentIndexPath.section];
                if (![self canMoveIndexPathFrom:indexPath toIndexPath:toIndexPath])return;
                
                self.currentMoveIndexPath = toIndexPath.copy;
                cell.currentIndexPath = toIndexPath.copy;
                [self moveRowFromIndexPath:indexPath toIndexPath:toIndexPath];
                [self.tableView moveRowAtIndexPath:indexPath toIndexPath:toIndexPath];
            } else {
                break;
            }
            
            //重新更新当前组的索引
            for (NSInteger i = 0; i < sectionCount + 1; i++) {
                SFMovableTableViewCell *e = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:c.currentIndexPath.section]];
                if (e) {
                    e.currentIndexPath = [NSIndexPath indexPathForRow:i inSection:c.currentIndexPath.section];
                }
            }
            for (NSInteger i = 0; i < srcSectionCount - 1; i++) {
                SFMovableTableViewCell *e = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:indexPath.section]];
                if (e) {
                    e.currentIndexPath = [NSIndexPath indexPathForRow:i inSection:indexPath.section];
                }
            }
            self.fromSection = intersectCellIndexPath.section;
            break;
        }
        
        //同组移动
        if ((CGRectGetMinY(toRect) < r.origin.y + r.size.height / 2.f &&
                                   CGRectGetMinY(toRect) >= r.origin.y)) {
            self.toSection = c.currentIndexPath.section;
            if (self.fromSection == self.toSection) {
                if (![self canMoveIndexPathFrom:indexPath toIndexPath:c.currentIndexPath])return;
                NSIndexPath *toIndexPath = c.currentIndexPath.copy;
                [self moveRowFromIndexPath:indexPath toIndexPath:c.currentIndexPath];
                self.currentMoveIndexPath = c.currentIndexPath;
                cell.currentIndexPath = c.currentIndexPath;
                c.currentIndexPath = indexPath;
                [self.tableView moveRowAtIndexPath:indexPath toIndexPath:toIndexPath];
            }
            break;
        }
    }
}

- (void)movableTableViewCell:(SFMovableTableViewCell*)cell endMoveAt:(CGPoint)p withIndexPath:(NSIndexPath *)indexPath {
    self.currentMoveIndexPath = nil;
}

- (void)movableTableViewCell:(SFMovableTableViewCell *)cell dealWithLongPress:(UILongPressGestureRecognizer *)gesture {
    if (self.moveTag != gesture && self.moveTag)return;
    if (state != gesture.state) {
        state = gesture.state;
        switch (state) {
            case UIGestureRecognizerStateBegan:     //开始移动的时候
            {
                cell.currentIndexPath = [self.tableView indexPathForCell:cell];
                self.currentLongPressGesture = gesture;
                self.startMoveSection = cell.currentIndexPath.section;
                self.moveTag = gesture;
                cell.customAlpha = 0;
                CGRect selfInScreenRect = [self.tableView convertRect:cell.frame toView:[UIApplication sharedApplication].keyWindow];
                if (!self.currentMovingView) {
                    UIImageView *iv = [[UIImageView alloc] initWithFrame:selfInScreenRect];
                    [[UIApplication sharedApplication].keyWindow addSubview:iv];
                    self.currentMovingView = iv;
                    //创建视图的截图，让截图在最上层让用户拖动
                    UIGraphicsBeginImageContext(selfInScreenRect.size);
                    [cell.contentView drawViewHierarchyInRect:cell.contentView.frame afterScreenUpdates:NO];
                    UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
                    UIGraphicsEndImageContext();
                    iv.image = snapshot;
                    iv.alpha = 0.6f;
                    iv.frame = selfInScreenRect;
                    iv.contentScaleFactor = [UIScreen mainScreen].scale;
                    iv.layer.shadowColor = [UIColor blackColor].CGColor;
                    iv.layer.shadowRadius = 5.f;
                    iv.layer.shadowOpacity = 0.4f;
                    iv.layer.shadowOffset = CGSizeMake(2.f, 2.f);
                    self.beginPoint = [gesture locationInView:cell];
                }
                self.currentMoveIndexPath = cell.currentIndexPath;
                self.fromSection          = cell.currentIndexPath.section;
                self.movingCell           = cell;
                break;
            }
            case UIGestureRecognizerStateFailed:
            case UIGestureRecognizerStateCancelled:
            case UIGestureRecognizerStateEnded:     //移动结束的时候
            {
                CGPoint endPoint = [gesture locationInView:[UIApplication sharedApplication].keyWindow];
                endPoint.x -= self.beginPoint.x;
                endPoint.y -= self.beginPoint.y;
                [self movableTableViewCell:cell endMoveAt:endPoint withIndexPath:self.currentMoveIndexPath];
                self.movingCell = nil;
                [self stopAutoScrollTimerIfNeed];
                self.tableView.scrollEnabled = NO;
                [UIView animateWithDuration:0.6f animations:^{
                    //动画复位
                    self.currentMovingView.frame = [self.tableView convertRect:cell.frame toView:[UIApplication sharedApplication].keyWindow];
                } completion:^(BOOL finished) {
                    if (finished) {
                        self.tableView.scrollEnabled = YES;
                        cell.customAlpha = 1.f;
                        [self.currentMovingView removeFromSuperview];
                        self.currentMovingView    = nil;
                        self.currentMoveIndexPath = nil;
                        //重新加载交换了两行的视图（更新currentIndexPath）
                        //如果没这句会造成下次移动错位
                        if (self.reloadDataWhenEndMove) {
                            [self.tableView reloadData];
                        } else if (self.fromSection >= 0 && self.toSection >= 0) {
                            if (self.fromSection == self.toSection && self.startMoveSection == self.fromSection) {
                                [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.fromSection] withRowAnimation:UITableViewRowAnimationNone];
                            } else {
                                NSMutableIndexSet *sets = [NSMutableIndexSet indexSet];
                                [sets addIndex:self.fromSection];
                                [sets addIndex:self.toSection];
                                if (self.startMoveSection != self.fromSection &&
                                    self.startMoveSection != self.toSection) {
                                    [sets addIndex:self.startMoveSection];
                                }
                                [self.tableView reloadSections:sets withRowAnimation:UITableViewRowAnimationNone];
                            }
                            self.startMoveSection = self.fromSection = self.toSection = -1;
                        }
                    }
                    self.moveTag                 = nil;
                    self.currentLongPressGesture = nil;
                }];
                break;
            }
                
            default:
                break;
        }
    }
    if (gesture.state == UIGestureRecognizerStateChanged) {     //拖动改变的时候
        if (self.currentMovingView) {  //移动镜像
            CGRect r = self.currentMovingView.frame;
            CGPoint screenPos = [gesture locationInView:[UIApplication sharedApplication].keyWindow];
            r.origin = screenPos;
            //偏移到中心位置
            r.origin.x -= self.beginPoint.x;
            r.origin.y -= self.beginPoint.y;
            self.currentMovingView.frame = r;
            [self movableTableViewCell:cell moveTo:r withIndexPath:self.currentMoveIndexPath];
            
            //是否自动滚动
            if (CGRectIntersectsRect(r, self.tableView.frame)) {
                if (screenPos.y >= CGRectGetHeight(self.tableView.frame) - 44.f * 1.5f) {
                    //向下移动
                    [self tryToAutoScroll:YES];
                } else if(screenPos.y <= 44.f * 1.5f) {
                    [self tryToAutoScroll:NO];
                } else {
                    [self stopAutoScrollTimerIfNeed];
                }
            } else {
                [self stopAutoScrollTimerIfNeed];
            }
        }
    }
}

#pragma mark Auto scroll

/**
 *  创建自动向下的滚动计时器
 *  @param isDown 是否向下，否则向上
 */
- (void)createTimerAutoScrollDown:(BOOL)isDown {
    self.autoScrollTimer = [NSTimer timerWithTimeInterval:1 / 60.f target:self selector:@selector(autoScroll:) userInfo:@(isDown) repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.autoScrollTimer forMode:NSRunLoopCommonModes];
}

/**
 *  自动向下移动
 */
- (void)autoScroll:(NSTimer*)timer {
    CGPoint offset = self.tableView.contentOffset;
    offset.y += [timer.userInfo boolValue] ? 5.f:-5.f;
    NSInteger sectionCount = [self.tableView numberOfSections];
    NSInteger lastRow = [self.tableView numberOfRowsInSection:sectionCount - 1];
    CGFloat lastSectionY = CGRectGetMinY([self.tableView rectForSection:sectionCount - 1]),
        lastRowY = 0;
    if (lastRow > 0) {
        lastRowY = CGRectGetMaxY([self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:lastRow - 1 inSection:sectionCount - 1]]);
    }
    if ((offset.y >=
         (lastRow > 0 ? lastRowY:lastSectionY) -
         CGRectGetHeight(self.tableView.frame) &&
         [timer.userInfo boolValue]
         ) ||
        (((offset.y < -CGRectGetHeight([self.tableView rectForSection:0]) &&
         [self.tableView numberOfRowsInSection:0] == 0) ||
          (offset.y < -CGRectGetHeight([self.tableView rectForHeaderInSection:0]) &&
           [self.tableView numberOfRowsInSection:0] > 0)) &&
         ![timer.userInfo boolValue]
         )
        ) {
        [self stopAutoScrollTimerIfNeed];
        return;
    }
    [self.tableView setContentOffset:offset animated:NO];
    [self movableTableViewCell:self.movingCell dealWithLongPress:self.currentLongPressGesture];
    [self movableTableViewDidAutoScroll];
}


#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    SFMovableTableViewCell *cell = nil;
    if (indexPath.section == self.currentMoveIndexPath.section &&
        indexPath.row == self.currentMoveIndexPath.row &&
        indexPath && self.movingCell) {
        cell = self.movingCell;
    } else {
        cell = [self cellFromIndexPath:indexPath];
        if (cell == self.movingCell) {
            cell = [self cellFromIndexPath:indexPath];
        }
    }
    
    cell.currentIndexPath = indexPath.copy;
    if (/*(cell.currentIndexPath.section == self.currentMoveIndexPath.section &&
        cell.currentIndexPath.row == self.currentMoveIndexPath.row &&
         self.currentMoveIndexPath) ||*/
        cell == self.movingCell) {
        cell.customAlpha = 0.f;
    } else {
        cell.customAlpha = 1.f;
    }
    cell.delegate = self;
    return cell;
}

#pragma mark - Getters

- (UITableView *)tableView {
    return (UITableView*)self.view;
}

- (BOOL)isAutoScrolling {
    return self.autoScrollTimer != nil;
}

@end
#pragma clang diagnostic pop
